﻿namespace ElBruno.MerryChristmas
{
    using Microsoft.Research.Kinect.Nui;

    public class DepthImageHelper
    {
        // We want to control how depth data gets converted into false-color data
        // for more intuitive visualization, so we keep 32-bit color frame buffer versions of
        // these, to be updated whenever we receive and process a 16-bit frame.
        private const int RedIndex = 2;
        private const int GreenIndex = 1;
        private const int BlueIndex = 0;
        private readonly byte[] depthFrame32 = new byte[320 * 240 * 4];

        /// <summary>
        /// Converts the depth frame. Converts a 16-bit grayscale depth frame which includes player indexes into a 32-bit frame that displays different players in different colors
        /// </summary>
        /// <param name="depthFrame16">The depth frame16.</param>
        /// <param name="runtimeOptions">The runtime options.</param>
        /// <returns></returns>
        public byte[] ConvertDepthFrame(byte[] depthFrame16, RuntimeOptions runtimeOptions)
        {
            var hasPlayerData = runtimeOptions.HasFlag(RuntimeOptions.UseDepthAndPlayerIndex);
            for (int i16 = 0, i32 = 0; i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4)
            {
                var player = hasPlayerData ? depthFrame16[i16] & 0x07 : -1;
                int realDepth;

                if (hasPlayerData)
                {
                    realDepth = (depthFrame16[i16 + 1] << 5) | (depthFrame16[i16] >> 3);
                }
                else
                {
                    realDepth = (depthFrame16[i16 + 1] << 8) | (depthFrame16[i16]);
                }

                // transform 13-bit depth information into an 8-bit intensity appropriate
                // for display (we disregard information in most significant bit)
                var intensity = (byte)(255 - (255 * realDepth / 0x0fff));

                depthFrame32[i32 + RedIndex] = 0;
                depthFrame32[i32 + GreenIndex] = 0;
                depthFrame32[i32 + BlueIndex] = 0;

                // choose different display colors based on player
                switch (player)
                {
                    case -1:
                    case 0:
                        depthFrame32[i32 + RedIndex] = (byte)(intensity / 2);
                        depthFrame32[i32 + GreenIndex] = (byte)(intensity / 2);
                        depthFrame32[i32 + BlueIndex] = (byte)(intensity / 2);
                        break;
                    case 1:
                        depthFrame32[i32 + RedIndex] = intensity;
                        break;
                    case 2:
                        depthFrame32[i32 + GreenIndex] = intensity;
                        break;
                    case 3:
                        depthFrame32[i32 + RedIndex] = (byte)(intensity / 4);
                        depthFrame32[i32 + GreenIndex] = intensity;
                        depthFrame32[i32 + BlueIndex] =  intensity;
                        break;
                    case 4:
                        depthFrame32[i32 + RedIndex] = intensity;
                        depthFrame32[i32 + GreenIndex] = intensity;
                        depthFrame32[i32 + BlueIndex] = (byte)(intensity / 4);
                        break;
                    case 5:
                        depthFrame32[i32 + RedIndex] = intensity;
                        depthFrame32[i32 + GreenIndex] = (byte)(intensity / 4);
                        depthFrame32[i32 + BlueIndex] = intensity;
                        break;
                    case 6:
                        depthFrame32[i32 + RedIndex] = (byte)(intensity / 2);
                        depthFrame32[i32 + GreenIndex] = (byte)(intensity / 2);
                        depthFrame32[i32 + BlueIndex] = intensity;
                        break;
                    case 7:
                        depthFrame32[i32 + RedIndex] = (byte)(255 - intensity);
                        depthFrame32[i32 + GreenIndex] = (byte)(255 - intensity);
                        depthFrame32[i32 + BlueIndex] = (byte)(255 - intensity);
                        break;
                }
            }
            return depthFrame32;
        }
    }
}